package com.mingyuean.demo.mybatis.binding;

import com.mingyuean.demo.mybatis.annotations.Param;
import com.mingyuean.demo.mybatis.annotations.Select;
import com.mingyuean.demo.mybatis.mapping.MappedStatement;
import com.mingyuean.demo.mybatis.mapping.SqlCommandType;
import com.mingyuean.demo.mybatis.parameter.GenericTokenParser;
import com.mingyuean.demo.mybatis.parameter.ParameterMapping;
import com.mingyuean.demo.mybatis.parameter.ParameterMappingTokenHandler;
import com.mingyuean.demo.mybatis.session.Configuration;
import com.mingyuean.demo.mybatis.session.SqlSession;
import com.mingyuean.demo.mybatis.type.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.*;
import java.sql.*;
import java.util.Date;
import java.util.*;

/**
 * @author MingYueAn
 * <p>  映射器方法
 * <p>  2023/3/20 15:37
 * @version: 1.0
 */
public class MapperMethod {
    private static final Logger log = LoggerFactory.getLogger(MapperMethod.class);

    /**
     * SQL指令
     */
    private final SqlCommand sqlCommand;

    /**
     * 构造方法
     *
     * @param mapperInterfaceClass 映射器接口
     * @param method               映射器方法
     * @param configuration        配置类
     */
    public MapperMethod(Method method, Class<?> mapperInterfaceClass, Configuration configuration) {
        // 创建 SQL 指令对象
        this.sqlCommand = new SqlCommand(method, mapperInterfaceClass, configuration);
        log.debug("MapperMethod SQL指令：sqlCommand = {}", sqlCommand);
    }

    /**
     * 执行SQL操作
     *
     * @param sqlSession SQL会话
     * @param args       参数列表
     * @return 返回结果
     */
    public Object execute(SqlSession sqlSession, Object[] args) throws SQLException, ClassNotFoundException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Object result = null;
        switch (sqlCommand.getType()) {
            // TODO: 2023/3/23 未完待续
            case INSERT:
                break;
            case DELETE:
                break;
            case UPDATE:
                break;
            case SELECT:
                log.debug("执行查询,{}", sqlCommand);
                result = sqlSession.selectOne(sqlCommand.getName(),args);
                // TODO: 2023/3/23 no
                // result = selectExecute(sqlCommand.getMethod(), args);
                break;
            default:
                throw new RuntimeException("未知的执行方法: " + sqlCommand.getName());
        }
        return result;
    }

    /**
     * SQL指令，封装了SQL语句的ID及类型
     */
    public static class SqlCommand {
        /**
         * SQL语句的ID
         */
        private final String name;
        /**
         * SQL语句类型
         */
        private final SqlCommandType type;

        private final Method method;

        /**
         * 构造方法
         *
         * @param configuration        配置类
         * @param mapperInterfaceClass 映射器接口
         * @param method               映射器方法
         */
        public SqlCommand(Method method, Class<?> mapperInterfaceClass, Configuration configuration) {
            // 根据映射器接口和方法的名称拼接成MappedStatement的唯一标识，获取其对应的MappedStatement对象
            String statementName = mapperInterfaceClass.getName() + "." + method.getName();
            log.debug("SQL语句的标识 namespaceId = {}", statementName);
            final MappedStatement mappedStatement = configuration.getMappedStatement(statementName);
            // 获取sql语句的ID
            this.name = mappedStatement.getId();
            // 获取sql语句的类型
            this.type = mappedStatement.getSqlCommandType();
            this.method = method;
        }

        @Override
        public String toString() {
            return "SqlCommand{" +
                    "name='" + name + '\'' +
                    ", type=" + type +
                    ", method=" + method +
                    '}';
        }

        public String getName() {
            return name;
        }

        public SqlCommandType getType() {
            return type;
        }

        public Method getMethod() {
            return method;
        }
    }

    // TODO: 2023/3/20 未完待续------------------------
    /**
     * 类型处理容器
     */
    private static Map<Class, TypeHandler> typeHandlerMap = new HashMap<>();

    static {
        typeHandlerMap.put(String.class, new StringTypeHandler());
        typeHandlerMap.put(Long.class, new LongTypeHandler());
        typeHandlerMap.put(Integer.class, new IntegerTypeHandler());
        typeHandlerMap.put(Date.class, new DateTypeHandler());
    }

    private Object selectExecute(Method method, Object[] args) throws SQLException, ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException {
        // 获取数据库连接---->获取预处理语句---->设置占位符---->执行预处理语句---->获取结果集---->结果集处理
        // 解析sql---->执行sql---->结果处理

        // TODO: 2023/3/12  获取数据库连接
        Connection connection = getConnection();

        // 判断方法是否存在Select注解
        if (method.isAnnotationPresent(Select.class)) {
            // 从@Select中获取sql语句
            final String sql = method.getAnnotation(Select.class).value();
            log.debug("未处理SQL语句 = " + sql);
            // 判断方法是否存在参数
            log.debug("参数数量：{}", method.getParameterCount());
            // 解析Sql语句，需要将 #{id} 解析
            final ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
            GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
            //获取处理后的结果
            String parseSql = genericTokenParser.parse(sql);
            log.debug("处理后的SQL语句 = " + parseSql);
            //获取参数名集合
            final List<ParameterMapping> parameterMappingList = parameterMappingTokenHandler.getParameterMappings();
            //参数名集合
            log.debug("有序的存储sql语句中的参数名集合：{}", String.valueOf(parameterMappingList));
            //----------------------------------------------------------
            // 创建一个存放 参数名:参数值 的集合
            final HashMap<String, Object> parameterNameValueMap = new HashMap<>();
            // 反射：获取方法中全部参数
            final Parameter[] parametersArray = method.getParameters();
            // 循环遍历参数数组
            for (int i = 0; i < parametersArray.length; i++) {
                // 判断是否存在注解@Param
                if (parametersArray[i].isAnnotationPresent(Param.class)) {
                    // 从注解中获取参数名
                    // 存入容器，key--名称，value--数值
                    parameterNameValueMap.put(parametersArray[i].getAnnotation(Param.class).value(), args[i]);
                }
                // 获取参数名arg0，获取参数值
                parameterNameValueMap.put(parametersArray[i].getName(), args[i]);
            }
            log.debug("存放【参数名:参数值】的集合：{}", parameterNameValueMap);
            //----------------------------------------------------------
            // 新建一个预处理语句对象
            final PreparedStatement preparedStatement = connection.prepareStatement(parseSql);
            // 将预处理语句中的占位符?用值替换
            for (int i = 0; i < parameterMappingList.size(); i++) {
                // 从集合中获取参数名
                final String content = parameterMappingList.get(i).getContent();
                final Object value = parameterNameValueMap.get(content);
                // 获取参数值的class类型
                final Class<?> typeClass = value.getClass();
                // 根据类型设置占位符【占位符替换】
                typeHandlerMap.get(typeClass).setParameter(preparedStatement, i + 1, value);
            }
            //----------------------------------------------------------
            // 获取方法返回值类型
            Class returnTypeClass = null;
            final Type returnType = method.getGenericReturnType();
            if (returnType instanceof Class) {
                // 不是泛型
                returnTypeClass = (Class) returnType;
            } else if (returnType instanceof ParameterizedType) {
                // 是泛型
                // https://blog.csdn.net/sageyin/article/details/114701550
                ParameterizedType type = (ParameterizedType) returnType;
                Type[] typeArray = type.getActualTypeArguments();
                log.debug("{}的{}", type, typeArray);
                returnTypeClass = (Class) typeArray[0];
            }
            log.debug("方法的返回值类型：{}", returnType);
            //----------------------------------------------------------
            Object resultObject = null;
            final List<Object> objectList = new ArrayList<>();
            // 执行预处理语句
            if (preparedStatement.execute()) {
                // 获取结果集
                final ResultSet resultSet = preparedStatement.getResultSet();
                // 获取结果集的元数据
                final ResultSetMetaData metaData = resultSet.getMetaData();
                // 获取结果集中所有字段
                final ArrayList<String> columnList = new ArrayList<>();
                for (int i = 0; i < metaData.getColumnCount(); i++) {
                    columnList.add(metaData.getColumnName(i + 1));
                }
                log.debug("结果集全部字段：{}", columnList);
                //==============================
                // 存放set方法的集合
                final HashMap<String, Method> setterMethodMap = new HashMap<>();
                for (Method declaredMethod : returnTypeClass.getDeclaredMethods()) {
                    // 判断方法名是否是以set开头
                    if (declaredMethod.getName().startsWith("set")) {
                        // 获取属性名
                        final String propertyName = declaredMethod.getName().substring(3);
                        // String.toLowerCase(Locale.ROOT)：https://blog.csdn.net/qq_45425667/article/details/115245107
                        final String name = propertyName.substring(0, 1).toLowerCase(Locale.ROOT) + propertyName.substring(1);
                        setterMethodMap.put(name, declaredMethod);
                    }
                }
                //==============================
                // 结果集处理
                while (resultSet.next()) {
                    // 构造方法创建对象
                    final Object obj = returnTypeClass.newInstance();
                    // 通过结果集的字段调用set方法
                    for (int i = 0; i < columnList.size(); i++) {
                        final String column = columnList.get(i);
                        // 从set方法集合中获取column对应的方法
                        final Method setterMethod = setterMethodMap.get(column);
                        // 根据方法的参数类型获取结果
                        setterMethod.invoke(obj,
                                typeHandlerMap
                                        .get(setterMethod.getParameterTypes()[0])
                                        .getResult(resultSet, column));
                    }
                    objectList.add(obj);
                }
            }
            connection.close();
            if (returnType instanceof Class) {
                // TODO: 2023/3/15 判断查询结果是否只有一条，如果不是会报错
                return resultObject = objectList.get(0);
            } else if (returnType instanceof ParameterizedType) {
                return resultObject = objectList;
            }

        }
        // TODO: 2023/3/15 未完待续
        connection.close();
        return null;
    }


    // TODO: 2023/3/12 获取连接
    private static Connection getConnection() throws SQLException, ClassNotFoundException {

        // 1.加载驱动
        Class.forName("com.mysql.jdbc.Driver");

        // 2.连接信息
        String url = "jdbc:mysql://localhost:3306/mya-mybatis?useUnicode=true&characterEncoding=utf8&useSSL=false";
        String username = "root";
        String password = "";

        // 3.连接成功
        return DriverManager.getConnection(url, username, password);
    }
}
